%{ 

#include "treenode.h"
#include "lex.yy.c"
treenode* root = NULL; 
treenode* Insert(char* syn, YYLTYPE loc, int num, ...);
int preErrLine = 0, curErrLine = 0;
int synErrType = SYN_ERR;

#define synErrAction(id) do { \
    synErrType = id; \
    if(unhandle > 0) \
    {    \
        fprintf(stderr,"%s.\n", synErrMsg[synErrType]); \
        synErrType = SYN_ERR; \
        --unhandle; \
        yyerrok; \
        } \
    } while (0) 

int unhandle = 0;
%}

%token TYPE STRUCT RETURN IF ELSE WHILE
%token INT FLOAT ID
%token SEMI COMMA ASSIGNOP RELOP PLUS MINUS STAR DIV AND OR DOT NOT LP RP LB RB LC RC

%locations
%right ASSIGNOP
%left OR
%left AND
%left RELOP
%left PLUS MINUS
%left STAR DIV
%right NOT
%left LP RP LB RB DOT
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE

%nonassoc LOWER_THAN_ERROR
%nonassoc error
%%
Program: ExtDefList { $$ = Insert("Program", @$, 1, $1); root = $$; }
    ;
ExtDefList: /* empty */ { $$ = Insert("ExtDefList", @$, 0); }
    | ExtDef ExtDefList { $$ = Insert("ExtDefList", @$, 2, $1, $2); }
    ;
ExtDef: Specifier ExtDecList SEMI { $$ = Insert("ExtDef", @$, 3, $1, $2, $3); }
    | Specifier SEMI { $$ = Insert("ExtDef", @$, 2, $1, $2); }
    | error SEMI { synErrAction(SYN_ERR); }
    | Specifier FunDec CompSt { $$ = Insert("ExtDef", @$, 3, $1, $2, $3); }
    ;
ExtDecList: VarDec { $$ = Insert("ExtDecList", @$, 1, $1); }
    | VarDec COMMA ExtDecList { $$ = Insert("ExtDecList", @$, 3, $1, $2, $3); }
    ;
Specifier: TYPE { $$ = Insert("Specifier", @$, 1, $1); }
    | StructSpecifier { $$ = Insert("Specifier", @$, 1, $1); }
    ;
StructSpecifier: STRUCT OptTag LC DefList RC { $$ = Insert("StructSpecifier", @$, 5, $1, $2, $3, $4, $5); }
    | STRUCT Tag { $$ = Insert("StructSpecifier", @$, 2, $1, $2); }
    ;
OptTag: /* empty */ { $$ = Insert("OptTag", @$, 0); }
    | ID { $$ = Insert("OptTag", @$, 1, $1); }
    ;
Tag: ID { $$ = Insert("Tag", @$, 1, $1); }
    ;
VarDec: ID { $$ = Insert("VarDec", @$, 1, $1); }
    | VarDec LB INT RB { $$ = Insert("VarDec", @$, 4, $1, $2, $3, $4); }
    ;
FunDec: ID LP VarList RP { $$ = Insert("FunDec", @$, 4, $1, $2, $3, $4); }
    | ID LP RP { $$ = Insert("FunDec", @$, 3, $1, $2, $3); }
    ;
VarList: ParamDec COMMA VarList { $$ = Insert("VarList", @$, 3, $1, $2, $3); }
    | ParamDec { $$ = Insert("VarList", @$, 1, $1); }
    ;
ParamDec: Specifier VarDec { $$ = Insert("ParamDec", @$, 2, $1, $2); }
    ;
CompSt: LC DefList StmtList RC { $$ = Insert("CompSt", @$, 4, $1, $2, $3, $4); }
    ;
StmtList: /* empty */ { $$ = Insert("StmtList", @$, 0); }
    | Stmt StmtList { $$ = Insert("StmtList", @$, 2, $1, $2); }
    ;
Stmt: Exp SEMI { $$ = Insert("Stmt", @$, 2, $1, $2); }
    | CompSt { $$ = Insert("Stmt", @$, 1, $1); }
    | RETURN Exp SEMI { $$ = Insert("Stmt", @$, 3, $1, $2, $3); }
    | IF LP Exp RP Stmt { $$ = Insert("Stmt", @$, 5, $1, $2, $3, $4, $5); }
    | IF LP Exp RP Stmt ELSE Stmt { $$ = Insert("Stmt", @$, 7, $1, $2, $3, $4, $5, $6, $7); }
    | WHILE LP Exp RP Stmt { $$ = Insert("Stmt", @$, 5, $1, $2, $3, $4, $5); }
    | error SEMI { synErrAction(INV_ST); }
    ;
DefList: /* empty */ { $$ = Insert("DefList", @$, 0); }
    | Def DefList { $$ = Insert("DefList", @$, 2, $1, $2); }
    ;
Def: Specifier DecList SEMI { $$ = Insert("Def", @$, 3, $1, $2, $3); }
    | error SEMI { synErrAction(INV_DEF); }
    ;
DecList: Dec { $$ = Insert("DecList", @$, 1, $1); }
    | Dec COMMA DecList { $$ = Insert("DecList", @$, 3, $1, $2, $3); }
    ;
Dec: VarDec { $$ = Insert("Dec", @$, 1, $1); }
    | VarDec ASSIGNOP Exp { $$ = Insert("Dec", @$, 3, $1, $2, $3); }
    ;
Exp: Exp ASSIGNOP Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp AND Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp OR Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp RELOP Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp PLUS Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp MINUS Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp STAR Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp DIV Exp { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | LP Exp RP { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | MINUS Exp %prec NOT { $$ = Insert("Exp", @$, 2, $1, $2); }
    | NOT Exp { $$ = Insert("Exp", @$, 2, $1, $2); }
    | ID LP Args RP { $$ = Insert("Exp", @$, 4, $1, $2, $3, $4); }
    | ID LP RP { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | Exp LB Exp RB { $$ = Insert("Exp", @$, 4, $1, $2, $3, $4); }
    | Exp DOT ID { $$ = Insert("Exp", @$, 3, $1, $2, $3); }
    | ID { $$ = Insert("Exp", @$, 1, $1); }
    | INT { $$ = Insert("Exp", @$, 1, $1); }
    | FLOAT { $$ = Insert("Exp", @$, 1, $1); }
    ;
Args: Exp COMMA Args { $$ = Insert("Args", @$, 3, $1, $2, $3); }
    | Exp { $$ = Insert("Args", @$, 1, $1); }
    ;
%%
yyerror(){
    err = 1;
    if(unhandle > 0)
    {
        fprintf(stderr,"%s.\n", synErrMsg[SYN_ERR]);--unhandle;
    }
    if(preErrLine != yylloc.first_line)
    {
        preErrLine = yylloc.first_line;
        fprintf(stderr,"Error type B at Line %d: ", yylloc.first_line);
        ++unhandle;
    }
}
void printSynMsg(int num)
{
    if(unhandle > 0)
    {
        //preErrLine = yylloc.first_line;
        fprintf(stderr,"%s.\n", synErrMsg[num]);
        synErrType = SYN_ERR;
        --unhandle;
        }
}
treenode* Insert(char* syn, YYLTYPE loc, int num, ...)
{
    treenode* parent = (treenode* )malloc(sizeof(treenode));
    parent->next = NULL;
    parent->terminal = 0;
    strcpy(parent->typename, syn);
    parent->capacity = num;
    parent->line = loc.first_line;
    if(num == 0)
        return parent;
    parent->next = (treenode** )malloc(sizeof(treenode*) * num);
    va_list vap;
    va_start(vap,num);
    for(int i = 0;i < num; i++)
    {
        parent->next[i] = va_arg(vap, treenode*);
    }
    va_end(vap);
    return parent;
}